﻿#region << 文 件 说 明 >>
/*----------------------------------------------------------------
// 文件名称：CreateCode
// 创 建 者：杨程
// 创建时间：2021/9/13 13:28:47
// 文件版本：V1.0.0
// ===============================================================
// 功能描述：
//		
//
//----------------------------------------------------------------*/
#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using V.CodeGenerator.CreateCodes.Model;

namespace V.CodeGenerator.CreateCodes
{
    /// <summary>
    /// 创建一整个完整的工程
    /// </summary>
    public static class CreateCodes
    {
        static CreateCodes()
        {

        }

        //public static bool IsCanBuild { get; set; } = false;
        /// <summary>
        /// 构建整个工程
        /// </summary>
        /// <param name="isCanBuild">是否可以构建</param>
        /// <param name="failedAction">构建失败方法</param>
        public static void Build(bool isCanBuild,Action<Exception> failedAction)
        {
            try
            {
                if (isCanBuild)
                {
                    Create_Solution();

                    Create_Project_Model();

                    Create_Project_View();

                    Create_Project_ViewModel();

                    Create_Project_DataAccess();
                }
            }
            catch (Exception ex)
            {
                failedAction?.Invoke(ex);
            }
            
        }


        #region 基本设置
        /// <summary>
        /// 命名空间
        /// </summary>
        public static string NameSpace { get; set; }

        /// <summary>
        /// 保存Solution的文件夹路径
        /// </summary>
        public static string SavePath { get; private set; }//

        /// <summary>
        /// 保存 View 层的路径
        /// </summary>
        private static string View_Project_SavePath { get; set; }

        /// <summary>
        /// 保存 ViewModel 层的路径
        /// </summary>
        private static string ViewModel_Project_SavePath { get; set; }

        /// <summary>
        /// 保存 Model 层的路径
        /// </summary>
        private static string Model_Project_SavePath { get; set; }

        /// <summary>
        /// 保存 DataAccess 层的路径
        /// </summary>
        private static string DataAccess_Project_SavePath { get; set; }


        private static string ProjectName { get; set; }
        /// <summary>
        /// 设置保存Solution层的路径
        /// </summary>
        public static void SetSavePath(string SaveFolderPath)
        {
            SavePath = SaveFolderPath;

            View_Project_SavePath = $"{SaveFolderPath}\\{NameSpace}.View";
            ViewModel_Project_SavePath = $"{SaveFolderPath}\\{NameSpace}.ViewModel";
            Model_Project_SavePath = $"{SaveFolderPath}\\{NameSpace}.Model";
            DataAccess_Project_SavePath = $"{SaveFolderPath}\\{NameSpace}.DataAccess";
            //Core_Project_SavePath = $"{SaveFolderPath}\\{NameSpace}.Core";
        }
        #endregion

        #region 模版文件名
        /// <summary>
        /// 数据操作层模版
        /// </summary>
        private static string DataAccess_Template = "DataAccess.DataAccessTemplate";

        /// <summary>
        /// Model层模版
        /// </summary>
        private static string Model_Template = "Model.ModelTemplate";

        /// <summary>
        /// Model层模版
        /// </summary>
        //private static string View_Template = "Model.ModelTemplate";

        /// <summary>
        /// Solution层模版
        /// </summary>
        private static string Solution_Template = "Solution.SolutionTemplate";

        /// <summary>
        /// Project层模版
        /// </summary>
        private static string Project_Template = "Project.ProjectTemplate";
        #endregion

        #region 创建各子项目
        /// <summary>
        /// 创建View项目
        /// </summary>
        private static void Create_Project_View()
        {
            CreateProject(true, ProjectType.View, View_Project_SavePath);

            CreateView_App();

            CreateView_ViewModelLocator();

            CreateView_LoginView();

            CreateView_MainView();
        }

        /// <summary>
        /// 创建ViewModel项目
        /// </summary>
        private static void Create_Project_ViewModel()
        {
            CreateProject(false, ProjectType.ViewModel, ViewModel_Project_SavePath);

            CreateViewModel_SystemDataContext();

            CreateViewModel_LoginViewModel();

            CreateViewModel_MainViewModel();
        }

        /// <summary>
        /// 创建Model项目
        /// </summary>
        private static void Create_Project_Model()
        {
            CreateProject(false, ProjectType.Model, Model_Project_SavePath);

            CreateModelPoco();
        }


        /// <summary>
        /// 创建DataAccess项目
        /// </summary>
        private static void Create_Project_DataAccess()
        {
            CreateProject(false, ProjectType.DataAccess, DataAccess_Project_SavePath);

            CreateDataAccess();
        }
        #endregion

        #region 获取模版信息
        /// <summary>
        /// 存储模版信息
        /// </summary>
        private static Dictionary<string, string> Templates = new Dictionary<string, string>();


        /// <summary>
        /// 获取模版文件并转成string
        /// </summary>
        /// <param name="FileName">文件名称</param>
        /// <returns>string</returns>
        private static string GetTemplateByFileName(string FileName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();

            string[] resNames = assembly.GetManifestResourceNames();
            using (Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.Templates.{FileName}.txt"))
            {
                if (stream != null)
                {
                    using (StreamReader sr = new StreamReader(stream))
                    {
                        string context = sr.ReadToEnd();
                        if (!Templates.ContainsKey(FileName))
                        {
                            Templates.Add(FileName, context);
                        }
                        
                        return context;
                    }
                }
                else
                {
                    return "";
                }

            }
        }
        #endregion

        #region 判断文件夹，如果没有则创建，有则跳过
        /// <summary>
        /// 判断文件是否存在，不存在则创建，类似：$"{SavePath}\\{NameSpace}.View"
        /// </summary>
        /// <param name="path">文件夹路径</param>
        private static void CheckFolder(string path)
        {
            if (!string.IsNullOrEmpty(path))
            {
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
            }
            else
            {
                throw new Exception("未设置Solution的保存地址！");
            }
        }
        #endregion

        /// <summary>
        /// 创建项目通用方法
        /// </summary>
        /// <param name="OutputType">输出类型，true为窗体，false为类库</param>
        /// <param name="projectType">项目类型</param>
        /// <param name="NameSpace">命名空间</param>
        /// <param name="ProjectSavePath">项目保存文件夹地址</param>
        private static void CreateProject(bool OutputType, ProjectType projectType, string ProjectSavePath)
        {
            if (string.IsNullOrEmpty(ProjectSavePath))
            {
                throw new Exception($"未设置{projectType}的保存地址！");
            }

            if (string.IsNullOrEmpty(NameSpace))
            {
                throw new Exception($"未设置 命名空间！");
            }


            string ProjectString = string.Empty;
            string ProjectName = string.Empty;

            StringBuilder sb = new StringBuilder();
            StringBuilder ProjectReferenceArea_sb = new StringBuilder();
            StringBuilder PackageReferenceArea_sb = new StringBuilder();

            //先获取到Project模版


            //看传入的是窗体还是类库，true是窗体，false是类库
            string outputType = OutputType == true ? "WinExe" : "library";

            switch (projectType)
            {
                case ProjectType.Model:
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Vampirewal.Core\" Version=\"1.0.0.13\" />");

                    ProjectName = $"{NameSpace}.Model.csproj";
                    break;
                case ProjectType.DataAccess:
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Microsoft.Data.Sqlite.Core\" Version=\"5.0.10\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Microsoft.EntityFrameworkCore\" Version=\"5.0.10\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Microsoft.EntityFrameworkCore.Sqlite\" Version=\"5.0.10\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Vampirewal.Core\" Version=\"1.0.0.19\" />");

                    ProjectReferenceArea_sb.AppendLine("    <ProjectReference Include=\"" + $"..\\{NameSpace}.Model\\{NameSpace}.Model.csproj" + "\" />");
                    //还有数据访问层
                    ProjectName = $"{NameSpace}.DataAccess.csproj";
                    break;
                case ProjectType.ViewModel:
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Vampirewal.Core\" Version=\"1.0.0.19\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"HandyControl\" Version=\"3.2.0\" />");

                    ProjectReferenceArea_sb.AppendLine("    <ProjectReference Include=\"" + $"..\\{NameSpace}.DataAccess\\{NameSpace}.DataAccess.csproj" + "\" />");
                    ProjectReferenceArea_sb.AppendLine("\t    <ProjectReference Include=\"" + $"..\\{NameSpace}.Model\\{NameSpace}.Model.csproj" + "\" />\r\n");
                    ProjectName = $"{NameSpace}.ViewModel.csproj";
                    break;
                case ProjectType.View:
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Microsoft.Xaml.Behaviors.Wpf\" Version=\"1.1.31\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"Vampirewal.Core\" Version=\"1.0.0.19\" />");
                    PackageReferenceArea_sb.AppendLine("    <PackageReference Include=\"HandyControl\" Version=\"3.2.0\" />");

                    ProjectReferenceArea_sb.AppendLine("    <ProjectReference Include=\"" + $"..\\{NameSpace}.ViewModel\\{NameSpace}.ViewModel.csproj" + "\" />");
                    ProjectReferenceArea_sb.AppendLine("\t    <ProjectReference Include=\"" + $"..\\{NameSpace}.Model\\{NameSpace}.Model.csproj" + "\" />");
                    ProjectName = $"{NameSpace}.View.csproj";
                    break;
                default:
                    break;
            }


            ProjectString = GetTemplateByFileName(Project_Template);

            ProjectString = ProjectString.Replace("${OutputType}", outputType)
                                         .Replace("${ProjectReferenceArea}", ProjectReferenceArea_sb.ToString())
                                         .Replace("${PackageReferenceArea}", PackageReferenceArea_sb.ToString());

            if (!string.IsNullOrEmpty(ProjectString))
            {

                CheckFolder(ProjectSavePath);

                StreamWriter sw = new StreamWriter($"{ProjectSavePath}\\{ProjectName}", false, Encoding.UTF8);
                sw.Write(ProjectString);
                sw.Close();


            }


        }

        /// <summary>
        /// 创建Solution
        /// </summary>
        private static void Create_Solution()
        {

            string SolutionGuid = Guid.NewGuid().ToString().ToUpper();

            string SolutionStr = string.Empty;

            StringBuilder ProjectArea_sb = new StringBuilder();

            ProjectArea_sb.AppendLine("Project(\"{${ProjectGuid}}\") = \"${NameSpace}.Model\", \"${NameSpace}.Model\\${NameSpace}.Model.csproj\", \"{${ModelGuid}}\"");
            ProjectArea_sb.AppendLine("EndProject");

            ProjectArea_sb.AppendLine("Project(\"{${ProjectGuid}}\") = \"${NameSpace}.DataAccess\", \"${NameSpace}.DataAccess\\${NameSpace}.DataAccess.csproj\", \"{${DataAccessGuid}}\"");
            ProjectArea_sb.AppendLine("EndProject");

            ProjectArea_sb.AppendLine("Project(\"{${ProjectGuid}}\") = \"${NameSpace}.ViewModel\", \"${NameSpace}.ViewModel\\${NameSpace}.ViewModel.csproj\", \"{${ViewModelGuid}}\"");
            ProjectArea_sb.AppendLine("EndProject");

            ProjectArea_sb.AppendLine("Project(\"{${ProjectGuid}}\") = \"${NameSpace}.View\", \"${NameSpace}.View\\${NameSpace}.View.csproj\", \"{${ViewGuid}}\"");
            ProjectArea_sb.AppendLine("EndProject");

            SolutionStr = GetTemplateByFileName(Solution_Template);

            SolutionStr = SolutionStr.Replace("${SolutionGuid}", SolutionGuid)
                                     .Replace("${ProjectArea}", ProjectArea_sb.ToString())
                                     .Replace("${ProjectGuid}", Guid.NewGuid().ToString().ToUpper())
                                     .Replace("${ModelGuid}", Guid.NewGuid().ToString().ToUpper())
                                     .Replace("${DataAccessGuid}", Guid.NewGuid().ToString().ToUpper())
                                     .Replace("${ViewModelGuid}", Guid.NewGuid().ToString().ToUpper())
                                     .Replace("${ViewGuid}", Guid.NewGuid().ToString().ToUpper())
                                     .Replace("${NameSpace}", NameSpace);


            CheckFolder(SavePath);

            using (StreamWriter sw = new StreamWriter($"{SavePath}\\{NameSpace}.sln", false, Encoding.UTF8))
            {
                sw.Write(SolutionStr);
            }

        }

        #region 创建Model.cs

        /// <summary>
        /// 存储需要生成的model
        /// </summary>
        public static List<ModelPoco> ModelPocos { get; set; } = new List<ModelPoco>();

        /// <summary>
        /// 创建model.cs文件
        /// </summary>
        public static void CreateModelPoco()
        {
            if (ModelPocos.Count <= 0)
            {
                throw new Exception("Model层未包含模型数据！无法创建！");
            }

            foreach (var ModelPoco in ModelPocos)
            {
                string ModelStr = string.Empty;
                if (!Templates.ContainsKey(Model_Template))
                {
                    ModelStr = GetTemplateByFileName(Model_Template);
                }
                else
                {
                    ModelStr = Templates[Model_Template];
                }

                StringBuilder ModelProp_sb = new StringBuilder();

                foreach (var item in ModelPoco.ModelDtlPocos)
                {
                    ModelProp_sb.AppendLine($"        private {item.propType} _{item.PropName} ;");
                    ModelProp_sb.AppendLine("        /// <summary>");
                    ModelProp_sb.AppendLine($"        ///{(string.IsNullOrEmpty(item.PropDes) == true ? item.PropName : item.PropDes)} ");
                    ModelProp_sb.AppendLine("        /// </summary>");
                    if (!string.IsNullOrEmpty(item.PropDes))
                    {
                        ModelProp_sb.AppendLine($"        [Display(Name=\"{item.PropDes}\")]");
                    }

                    if (item.MinValue > 0)
                    {
                        ModelProp_sb.AppendLine($"        [MinLength({item.MinValue},ErrorMessage =\"最小值为{item.MinValue}！\")]");
                        ModelProp_sb.AppendLine($"        [MaxLength({item.MaxValue},ErrorMessage =\"最大值为{item.MaxValue}！\")]");
                    }
                    if (item.IsRequired)
                    {
                        ModelProp_sb.AppendLine($"        [Required(AllowEmptyStrings = false, ErrorMessage = \"字段不能为空！\")]");
                    }
                    if (item.IsCreateDbProp)
                    {
                        ModelProp_sb.AppendLine($"        [NotMapped]");
                    }
                    ModelProp_sb.AppendLine($"        public {item.propType} {item.PropName}");
                    ModelProp_sb.AppendLine("        {");
                    ModelProp_sb.AppendLine("            get{ return" + $" _{item.PropName}" + ";}");
                    ModelProp_sb.AppendLine("            set{" + $" _{item.PropName}=value;DoNotify();" + "}");
                    ModelProp_sb.AppendLine("        }");

                    ModelProp_sb.AppendLine();

                }





                ModelStr = ModelStr.Replace("${NameSpace}", NameSpace)
                                  .Replace("${TableName}", ModelPoco.TableName)
                                  .Replace("${ClassDes}",ModelPoco.DisplayName)
                                  .Replace("${ClassName}", ModelPoco.ClassName)
                                  .Replace("${InheritanceBaseModel}", ModelPoco.InheritanceBaseModel)
                                  .Replace("${currentDateTime}", DateTime.Now.ToString())
                                  .Replace("${Props}", ModelProp_sb.ToString());

                CheckFolder($"{Model_Project_SavePath}\\Model");

                using (StreamWriter sw = new StreamWriter($"{Model_Project_SavePath}\\Model\\{ModelPoco.ClassName}.cs", false, Encoding.UTF8))
                {
                    sw.Write(ModelStr);
                }
            }
        }

        #endregion

        #region 创建DataContext.cs
        /// <summary>
        /// 创建数据库上下文
        /// </summary>
        private static void CreateDataAccess()
        {
            if (ModelPocos.Count <= 0)
            {
                throw new Exception("Model层未包含模型数据！无法创建！");
            }

            string DataAccessStr = string.Empty;
            if (!Templates.ContainsKey(DataAccess_Template))
            {
                DataAccessStr = GetTemplateByFileName(DataAccess_Template);
            }
            else
            {
                DataAccessStr = Templates[DataAccess_Template];
            }

            StringBuilder DataAccess_sb = new StringBuilder();

            foreach (ModelPoco modelPoco in ModelPocos)
            {
                DataAccess_sb.AppendLine();
                DataAccess_sb.AppendLine("        /// <summary>");
                DataAccess_sb.AppendLine($"        ///{modelPoco.ClassName}数据库");
                DataAccess_sb.AppendLine("        /// </summary>");
                DataAccess_sb.AppendLine($"        public DbSet<{modelPoco.ClassName}> {modelPoco.ClassName}s {{ get; set; }}");
                DataAccess_sb.AppendLine();
            }

            DataAccessStr = DataAccessStr.Replace("${NameSpace}", NameSpace)
                                        .Replace("${CurrentDateTime}", DateTime.Now.ToString())
                                        .Replace("${DbSet}", DataAccess_sb.ToString());

            CheckFolder($"{DataAccess_Project_SavePath}");

            using (StreamWriter sw = new StreamWriter($"{DataAccess_Project_SavePath}\\DataContext.cs", false, Encoding.UTF8))
            {
                sw.Write(DataAccessStr);
            }
        }
        #endregion

        #region 创建View层内容

        /// <summary>
        /// 创建View层中的app
        /// </summary>
        private static void CreateView_App()
        {
            string app_cs_str = GetTemplateByFileName("View.App.AppTemplate_CS");
            string app_xaml_str = GetTemplateByFileName("View.App.AppTemplate_XAML");

            #region 先处理cs
            app_cs_str = app_cs_str.Replace("${NameSpace}", NameSpace);
            #endregion

            #region 处理xaml
            app_xaml_str = app_xaml_str.Replace("${NameSpace}", NameSpace);
            #endregion

            CheckFolder(View_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\App.xaml.cs", false, Encoding.UTF8))
            {
                sw.Write(app_cs_str);
            }

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\App.xaml", false, Encoding.UTF8))
            {
                sw.Write(app_xaml_str);
            }
        }

        private static void CreateView_ViewModelLocator()
        {
            string ViewModelLocator= GetTemplateByFileName("View.ViewModelLocator_Template");

            ViewModelLocator = ViewModelLocator.Replace("${NameSpace}", NameSpace)
                                               .Replace("${currentDateTime}",DateTime.Now.ToString());

            CheckFolder(View_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\ViewModelLocator.cs", false, Encoding.UTF8))
            {
                sw.Write(ViewModelLocator);
            }
        }

        /// <summary>
        /// 创建View层中的loginview
        /// </summary>
        private static void CreateView_LoginView()
        {
            string login_cs_str= GetTemplateByFileName("View.ViewCs_Template");
            string login_xaml_str= GetTemplateByFileName("View.LoginView.LoginViewTemplate_XAML");

            #region 处理cs
            login_cs_str = login_cs_str.Replace("${NameSpace}", NameSpace)
                                       .Replace("${ViewName}", "LoginView")
                                       .Replace("${InheritanceBaseModel}", "HandyControl.Controls.Window")
                                       .Replace("${currentDateTime}",DateTime.Now.ToString());
            #endregion

            #region 处理xaml
            login_xaml_str = login_xaml_str.Replace("${NameSpace}", NameSpace);
            #endregion

            CheckFolder(View_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\LoginView.xaml.cs", false, Encoding.UTF8))
            {
                sw.Write(login_cs_str);
            }

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\LoginView.xaml", false, Encoding.UTF8))
            {
                sw.Write(login_xaml_str);
            }
        }

        /// <summary>
        /// 创建View层中的MainView
        /// </summary>
        private static void CreateView_MainView()
        {
            string Main_cs_str = GetTemplateByFileName("View.ViewCs_Template");
            string Main_xaml_str = GetTemplateByFileName("View.MainView.MainViewTemplate_XAML");

            Main_cs_str = Main_cs_str.Replace("${NameSpace}", NameSpace)
                                       .Replace("${ViewName}", "MainView")
                                       .Replace("${InheritanceBaseModel}", "HandyControl.Controls.Window")
                                       .Replace("${currentDateTime}", DateTime.Now.ToString());

            Main_xaml_str = Main_xaml_str.Replace("${NameSpace}", NameSpace);

            CheckFolder(View_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\MainView.xaml.cs", false, Encoding.UTF8))
            {
                sw.Write(Main_cs_str);
            }

            using (StreamWriter sw = new StreamWriter($"{View_Project_SavePath}\\MainView.xaml", false, Encoding.UTF8))
            {
                sw.Write(Main_xaml_str);
            }
        }
        #endregion

        #region 创建ViewModel层的内容
        /// <summary>
        /// 创建Loginviewmodel
        /// </summary>
        private static void CreateViewModel_LoginViewModel()
        {
            string loginviewmodel_str= GetTemplateByFileName("ViewModel.LoginViewModel");

            loginviewmodel_str = loginviewmodel_str.Replace("${NameSpace}", NameSpace)
                                                   .Replace("${currentDateTime}",DateTime.Now.ToString());

            CheckFolder(ViewModel_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{ViewModel_Project_SavePath}\\LoginViewModel.cs", false, Encoding.UTF8))
            {
                sw.Write(loginviewmodel_str);
            }
        }

        /// <summary>
        /// 创建数据中心
        /// </summary>
        private static void CreateViewModel_SystemDataContext()
        {
            string SystemDataContext_str= GetTemplateByFileName("ViewModel.SystemDataContext");

            SystemDataContext_str = SystemDataContext_str.Replace("${NameSpace}", NameSpace)
                                                         .Replace("${currentDateTime}", DateTime.Now.ToString());

            CheckFolder(ViewModel_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{ViewModel_Project_SavePath}\\SystemDataContext.cs", false, Encoding.UTF8))
            {
                sw.Write(SystemDataContext_str);
            }
        }

        /// <summary>
        /// 创建MainViewModel
        /// </summary>
        private static void CreateViewModel_MainViewModel()
        {
            string MainViewModel_str = GetTemplateByFileName("ViewModel.MainViewModel");

            MainViewModel_str = MainViewModel_str.Replace("${NameSpace}", NameSpace)
                                                 .Replace("${currentDateTime}", DateTime.Now.ToString());

            CheckFolder(ViewModel_Project_SavePath);

            using (StreamWriter sw = new StreamWriter($"{ViewModel_Project_SavePath}\\MainViewModel.cs", false, Encoding.UTF8))
            {
                sw.Write(MainViewModel_str);
            }
        }
        #endregion
    }

    /// <summary>
    /// 创建工程的时候，工程类型
    /// </summary>
    public enum ProjectType
    {
        /// <summary>
        /// model层
        /// </summary>
        Model = 0,
        /// <summary>
        /// DataAccess层
        /// </summary>
        DataAccess = 1,
        /// <summary>
        /// ViewModel层
        /// </summary>
        ViewModel = 2,
        /// <summary>
        /// View层
        /// </summary>
        View = 3
    }
}
